//
//  ITPingTool.m
//  InduceTool
//
//  Created by 魏延龙 on 2020/8/28.
//  Copyright © 2020 魏延龙. All rights reserved.
//

#import "ITPingTool.h"
#import <PPSPing/PPSPing.h>

@interface ITPingTool()<PPSPingDelegate>
{
    BOOL _hasStarted;
}

@property (nonatomic, strong) PPSPing *ping;
@property (nonatomic, copy)   NSString   *address;

// 最大发包次数
@property (nonatomic, assign) NSInteger  maximumPingTimes;
@property (nonatomic, strong) NSMutableArray *pingItems;

// 默认 once
@property (nonatomic, assign) PingUseType pingUseType;
@end

@implementation ITPingTool

+ (instancetype)serviceWithAddress:(NSString *)address {
    ITPingTool *services = [[ITPingTool alloc] initWithAddress:address];
    return services;
}

+(instancetype)serviceWithAddress:(NSString *)address maximumPingTimes:(NSInteger)count pingUseType:(PingUseType)pingUseType {
    ITPingTool *services = [[ITPingTool alloc] initWithAddress:address maximumPingTimes:count pingUseType:pingUseType];
    return services;
}

- (instancetype)initWithAddress:(NSString *)address{
    self = [self initWithAddress:address maximumPingTimes:20 pingUseType:PingUseTypeOnce];
    return self;
}

- (instancetype)initWithAddress:(NSString *)address maximumPingTimes:(NSInteger)maximumPingTimes pingUseType:(PingUseType)pingUseType{
    self = [super init];
    if (self) {
        _maximumPingTimes = maximumPingTimes;
        _pingUseType = pingUseType;
        _address = [address copy];
        _ping = [PPSPing new];
        _ping.host = _address;
        _ping.delegate = self;
        _ping.timeout = 1;
        _ping.pingPeriod = 0.5;
        self.pingItems = [NSMutableArray arrayWithCapacity:maximumPingTimes];
    }
    return self;
}


- (void)startPing {
    [self.pingItems removeAllObjects];
    __weak typeof(self) weakSelf = self;
    [self.ping setupWithBlock:^(BOOL success, NSError *error) {
        if (success) {
            [weakSelf.ping startPinging];
        } else {
            NSLog(@"ping失败");
            PPSPingSummary *summary = [[PPSPingSummary alloc] init];
            summary.status = PPSPingStatusFinished;
            [weakSelf handlSummary:summary];
        }
    }];
}

- (void)cancel {
    [self.ping stop];
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(startPing) object:nil];
}

-(void)ping:(PPSPing *)pinger didFailWithError:(NSError *)error {
    PPSPingSummary *summary = [[PPSPingSummary alloc] init];
    summary.status = PPSPingStatusError;
    [self handlSummary:summary];
}

-(void)ping:(PPSPing *)pinger didTimeoutWithSummary:(PPSPingSummary *)summary {
    [self handlSummary:summary];
}

-(void)ping:(PPSPing *)pinger didSendPingWithSummary:(PPSPingSummary *)summary {
    if (_hasStarted) {
        _hasStarted = YES;
        [self handlSummary:summary];
    }
}

-(void)ping:(PPSPing *)pinger didReceiveReplyWithSummary:(PPSPingSummary *)summary {
    [self handlSummary:summary];
}

-(void)ping:(PPSPing *)pinger didFailToSendPingWithSummary:(PPSPingSummary *)summary error:(NSError *)error {
    [self handlSummary:summary];
}

-(void)ping:(PPSPing *)pinger didReceiveUnexpectedReplyWithSummary:(PPSPingSummary *)summary {
    [self handlSummary:summary];
}

- (void)handlSummary:(PPSPingSummary *)summary {
    NSLog(@"ITPing %@", summary);
    if (self.pingUseType == PingUseTypeOnce) {
        [self.pingItems addObject:summary];
        if (self.pingItems.count > 9) {
            [self cancel];
            NetworkStatus status = [self varianceValue:self.pingItems];
            if (self.networkStatusBlock) {
                self.networkStatusBlock(status);
            }
        }
    } else if (self.pingUseType == PingUseTypeLoop) {
        if (self.pingItems.count > 9) {
            [self varianceValue:self.pingItems];
        }
    }
}

- (NetworkStatus)varianceValue:(NSArray<PPSPingSummary *> *)array {
    NSMutableArray *timeArr = [NSMutableArray array];
    __block double sum = 0;
    __block double maxValue = 0;
    __block double minValue = 0;
    [array enumerateObjectsUsingBlock:^(PPSPingSummary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        double value = -1;
        switch (obj.status) {
            case PPSPingStatusDidStart:
                value = 1000;
                break;
            case PPSPingStatusDidFailToSendPacket:
                value = 1000;
                break;
            case PPSPingStatusDidReceivePacket:
                value = obj.rtt;
                break;
            case PPSPingStatusDidReceiveUnexpectedPacket:
                value = 1000;
                break;
            case PPSPingStatusDidTimeout:
                value = 1000;
                break;
            case PPSPingStatusError:
                value = 1000;
                break;
            case PPSPingStatusFinished:
                
                break;
            default:
                break;
        }
        
        if (value > maxValue) {
            maxValue = value;
        }
        
        if (value < minValue) {
            minValue = value;
        }
        
        if (value != -1) {
            [timeArr addObject:[NSNumber numberWithDouble:value]];
            sum += value;
        }
    }];
    
    double average = sum / timeArr.count;
    
    if (maxValue < 100) {
        // 当响应时间最大值小于100 认为网络良好
        NSLog(@"ITPingTool 检测到网络良好");
        return NetworkStatusGood;
    }
    
    if (average > 800) {
        // 当平均值大于800ms时 认为网络不好
        NSLog(@"ITPingTool 检测到网络很差");
        return NetworkStatusBad;
    }
    
    if (timeArr.count > 0) {
        __block double sumValue = 0;
        [timeArr enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            double value = [obj doubleValue];
            double difference = value - average;
            double powValue = pow(difference, 2);
            sumValue += powValue;
        }];
        double result = (sumValue/timeArr.count);
        double coefficient = result/average;
        NSLog(@"ITPingTool 网络抖动参数 方差值： %f , 平均值 %f,变异系数 %f",result, average, result/average);
        if (coefficient > 10) {
            NSLog(@"ITPingTool 检测到网络很差");
            return NetworkStatusBad;
        } else {
            NSLog(@"ITPingTool 检测到网络良好");
            return NetworkStatusGood;
        }
    } else {
        NSLog(@"ITPingTool 检测到网络很差");
        return NetworkStatusBad;
    }
    
}


@end
